---
title: Python custom provider
subtitle: Learn how to create custom providers for any AI framework in Python
image: "https://og.composio.dev/api/og?title=Python%20custom%20provider"   # image for socials
keywords: ""
hide-nav-links: false
---

This guide shows how to create custom providers for the Composio Python SDK. Custom providers enable integration with different AI frameworks and platforms.

## Provider architecture

The Composio SDK uses a provider architecture to adapt tools for different AI frameworks. The provider handles:

1. **Tool format transformation**: Converting Composio tools into formats compatible with specific AI platforms
2. **Platform-specific integration**: Providing helper methods for seamless integration

## Types of providers

There are two types of providers:

1. **Non-agentic providers**: Transform tools for platforms that don't have their own agency (e.g., OpenAI, Anthropic)
2. **Agentic providers**: Transform tools for platforms that have their own agency (e.g., LangChain, CrewAI)

## Provider class hierarchy

```
BaseProvider (Abstract)
├── NonAgenticProvider (Abstract)
│   └── OpenAIProvider (Concrete)
│   └── AnthropicProvider (Concrete)
│   └── [Your Custom Non-Agentic Provider] (Concrete)
└── AgenticProvider (Abstract)
    └── LangchainProvider (Concrete)
    └── [Your Custom Agentic Provider] (Concrete)
```

## Quick start

The fastest way to create a new provider is using the provider scaffolding script:

```bash
# Create a non-agentic provider
make create-provider name=myprovider

# Create an agentic provider
make create-provider name=myagent agentic=true

# Create provider with custom output directory
make create-provider name=myprovider output=/path/to/custom/dir

# Combine options
make create-provider name=myagent agentic=true output=/my/custom/path
```

This will create a new provider in the specified directory (default: `python/providers/<provider-name>/`) with:
- Complete package structure with `pyproject.toml`
- Provider implementation template
- Demo script
- README with usage examples
- Type annotations and proper inheritance

<Tip>
The scaffolding script creates a fully functional provider template. You just need to implement the tool transformation logic specific to your platform. You can maintain your provider implementation in your own repository.
</Tip>

### Generated structure

The create-provider script generates the following structure:

```
python/providers/<provider-name>/
├── README.md                    # Documentation and usage examples
├── pyproject.toml              # Project configuration
├── setup.py                    # Setup script for pip compatibility
├── <provider-name>_demo.py     # Demo script showing usage
└── composio_<provider-name>/   # Package directory
    ├── __init__.py             # Package initialization
    ├── provider.py             # Provider implementation
    └── py.typed                # PEP 561 type marker
```

After generation, you can:
1. Navigate to the provider directory: `cd python/providers/<provider-name>`
2. Install in development mode: `uv pip install -e .`
3. Implement your provider logic in `composio_<provider-name>/provider.py`
4. Test with the demo script: `python <provider-name>_demo.py`

## Creating a non-agentic provider

Non-agentic providers inherit from the `NonAgenticProvider` abstract class:

```python
from typing import List, Optional, Sequence, TypeAlias
from composio.core.provider import NonAgenticProvider
from composio.types import Tool, Modifiers, ToolExecutionResponse

# Define your tool format
class MyAITool:
    def __init__(self, name: str, description: str, parameters: dict):
        self.name = name
        self.description = description
        self.parameters = parameters

# Define your tool collection format
MyAIToolCollection: TypeAlias = List[MyAITool]

# Create your provider
class MyAIProvider(NonAgenticProvider[MyAITool, MyAIToolCollection], name="my-ai-platform"):
    """Custom provider for My AI Platform"""
    
    def wrap_tool(self, tool: Tool) -> MyAITool:
        """Transform a single tool to platform format"""
        return MyAITool(
            name=tool.slug,
            description=tool.description or "",
            parameters={
                "type": "object",
                "properties": tool.input_parameters.get("properties", {}),
                "required": tool.input_parameters.get("required", [])
            }
        )
    
    def wrap_tools(self, tools: Sequence[Tool]) -> MyAIToolCollection:
        """Transform a collection of tools"""
        return [self.wrap_tool(tool) for tool in tools]
    
    # Optional: Custom helper methods for your AI platform
    def execute_my_ai_tool_call(
        self,
        user_id: str,
        tool_call: dict,
        modifiers: Optional[Modifiers] = None
    ) -> ToolExecutionResponse:
        """Execute a tool call in your platform's format
        
        Example usage:
        result = my_provider.execute_my_ai_tool_call(
            user_id="default",
            tool_call={"name": "GITHUB_STAR_REPO", "arguments": {"owner": "composiohq", "repo": "composio"}}
        )
        """
        # Use the built-in execute_tool method
        return self.execute_tool(
            slug=tool_call["name"],
            arguments=tool_call["arguments"],
            modifiers=modifiers,
            user_id=user_id
        )
```

## Creating an agentic provider

Agentic providers inherit from the `AgenticProvider` abstract class:

```python
from typing import Callable, Dict, List, Sequence
from composio.core.provider import AgenticProvider, AgenticProviderExecuteFn
from composio.types import Tool
from my_provider import AgentTool

# Import the Tool/Function class that represents a callable tool for your framework
# Optionally define your custom tool format below
# class AgentTool:
#     def __init__(self, name: str, description: str, execute: Callable, schema: dict):
#         self.name = name
#         self.description = description
#         self.execute = execute
#         self.schema = schema

# Define your tool collection format (typically a List)
AgentToolCollection: TypeAlias = List[AgentTool]

# Create your provider
class MyAgentProvider(AgenticProvider[AgentTool, List[AgentTool]], name="my-agent-platform"):
    """Custom provider for My Agent Platform"""
    
    def wrap_tool(self, tool: Tool, execute_tool: AgenticProviderExecuteFn) -> AgentTool:
        """Transform a single tool with execute function"""
        def execute_wrapper(**kwargs) -> Dict:
            result = execute_tool(tool.slug, kwargs)
            if not result.get("successful", False):
                raise Exception(result.get("error", "Tool execution failed"))
            return result.get("data", {})
        
        return AgentTool(
            name=tool.slug,
            description=tool.description or "",
            execute=execute_wrapper,
            schema=tool.input_parameters
        )
    
    def wrap_tools(
        self, 
        tools: Sequence[Tool], 
        execute_tool: AgenticProviderExecuteFn
    ) -> AgentToolCollection:
        """Transform a collection of tools with execute function"""
        return [self.wrap_tool(tool, execute_tool) for tool in tools]
```

## Using your custom provider

After creating your provider, use it with the Composio SDK:

### Non-agentic provider example

```python
from composio import Composio
from composio_myai import MyAIProvider
from myai import MyAIClient  # Your AI platform's client

# Initialize tools
myai_client = MyAIClient()
composio = Composio(provider=MyAIProvider())

# Define task
task = "Star a repo composiohq/composio on GitHub"

# Get GitHub tools that are pre-configured
tools = composio.tools.get(user_id="default", toolkits=["GITHUB"])

# Get response from your AI platform (example)
response = myai_client.chat.completions.create(
    model="your-model",
    tools=tools,  # These are in your platform's format
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": task},
    ],
)
print(response)

# Execute the function calls
result = composio.provider.handle_tool_calls(response=response, user_id="default")
print(result)
```

### Agentic provider example

```python
import asyncio
from agents import Agent, Runner
from composio_myagent import MyAgentProvider
from composio import Composio

# Initialize Composio toolset
composio = Composio(provider=MyAgentProvider())

# Get all the tools
tools = composio.tools.get(
    user_id="default",
    tools=["GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"],
)

# Create an agent with the tools
agent = Agent(
    name="GitHub Agent",
    instructions="You are a helpful assistant that helps users with GitHub tasks.",
    tools=tools,
)

# Run the agent
async def main():
    result = await Runner.run(
        starting_agent=agent,
        input=(
            "Star the repository composiohq/composio on GitHub. If done "
            "successfully, respond with 'Action executed successfully'"
        ),
    )
    print(result.final_output)

asyncio.run(main())
```


## Example: Anthropic Claude provider

Here's a complete example for Anthropic's Claude:

```python
import typing as t

from anthropic.types.beta.beta_tool_use_block import BetaToolUseBlock
from anthropic.types.message import Message as ToolsBetaMessage
from anthropic.types.tool_param import ToolParam
from anthropic.types.tool_use_block import ToolUseBlock

from composio.core.provider import NonAgenticProvider
from composio.types import Modifiers, Tool, ToolExecutionResponse


class AnthropicProvider(
    NonAgenticProvider[ToolParam, list[ToolParam]],
    name="anthropic",
):
    """Composio toolset for Anthropic Claude platform."""

    def wrap_tool(self, tool: Tool) -> ToolParam:
        return ToolParam(
            input_schema=tool.input_parameters,
            name=tool.slug,
            description=tool.description,
        )

    def wrap_tools(self, tools: t.Sequence[Tool]) -> list[ToolParam]:
        return [self.wrap_tool(tool) for tool in tools]

    def execute_tool_call(
        self,
        user_id: str,
        tool_call: ToolUseBlock,
        modifiers: t.Optional[Modifiers] = None,
    ) -> ToolExecutionResponse:
        """Execute a tool call.
        
        :param user_id: User ID to use for executing function calls.
        :param tool_call: Tool call metadata.
        :param modifiers: Modifiers to use for executing function calls.
        :return: Object containing output data from the tool call.
        """
        return self.execute_tool(
            slug=tool_call.name,
            arguments=t.cast(t.Dict, tool_call.input),
            modifiers=modifiers,
            user_id=user_id,
        )

    def handle_tool_calls(
        self,
        user_id: str,
        response: t.Union[dict, ToolsBetaMessage],
        modifiers: t.Optional[Modifiers] = None,
    ) -> t.List[ToolExecutionResponse]:
        """Handle tool calls from Anthropic Claude chat completion object.
        
        :param response: Chat completion object from
            `anthropic.Anthropic.beta.tools.messages.create` function call.
        :param user_id: User ID to use for executing function calls.
        :param modifiers: Modifiers to use for executing function calls.
        :return: A list of output objects from the tool calls.
        """
        if isinstance(response, dict):
            response = ToolsBetaMessage(**response)

        outputs = []
        for content in response.content:
            if isinstance(content, (ToolUseBlock, BetaToolUseBlock)):
                outputs.append(
                    self.execute_tool_call(
                        user_id=user_id,
                        tool_call=content,
                        modifiers=modifiers,
                    )
                )
        return outputs
```

## Example: OpenAI Agents provider

Here's an example for OpenAI Agents framework:

```python
import asyncio
import json
import typing as t

import pydantic
from agents import FunctionTool

from composio.core.provider import AgenticProvider, AgenticProviderExecuteFn
from composio.types import Tool
from composio.utils.pydantic import parse_pydantic_error


class OpenAIAgentsProvider(
    AgenticProvider[FunctionTool, list[FunctionTool]],
    name="openai_agents",
):
    """Provider for OpenAI Agents framework"""
    
    def wrap_tool(
        self,
        tool: Tool,
        execute_tool: AgenticProviderExecuteFn,
    ) -> FunctionTool:
        """Wrap a tool as a FunctionTool"""
        
        # Create an async wrapper for the tool execution
        async def execute_tool_wrapper(_ctx, payload):
            """Execute Composio action with the given arguments"""
            try:
                return json.dumps(
                    obj=(
                        await asyncio.to_thread(  # Running in thread since execute_tool is not async
                            execute_tool,
                            slug=tool.slug,
                            arguments=json.loads(payload) if payload else {},
                        )
                    )
                )
            except pydantic.ValidationError as e:
                return json.dumps({
                    "successful": False,
                    "error": parse_pydantic_error(e),
                    "data": None,
                })
            except Exception as e:
                return json.dumps({
                    "successful": False,
                    "error": str(e),
                    "data": None,
                })
        
        # Ensure the schema has additionalProperties set to false
        modified_schema = tool.input_parameters.copy()
        modified_schema["additionalProperties"] = False
        
        # Create a FunctionTool with the appropriate schema
        return FunctionTool(
            name=tool.slug,
            description=tool.description,
            params_json_schema=modified_schema,
            on_invoke_tool=execute_tool_wrapper,
            strict_json_schema=False,  # Composio tools already have optimal schemas
        )
    
    def wrap_tools(
        self,
        tools: t.Sequence[Tool],
        execute_tool: AgenticProviderExecuteFn,
    ) -> list[FunctionTool]:
        """Wrap multiple tools for OpenAI Agents"""
        return [self.wrap_tool(tool, execute_tool) for tool in tools]
```

## Best practices

1. **Keep providers focused**: Each provider should integrate with one specific platform
2. **Handle errors gracefully**: Catch and transform errors from tool execution
3. **Follow platform conventions**: Adopt naming and structural conventions of the target platform
4. **Use type annotations**: Leverage Python's typing system for better IDE support and documentation
5. **Cache transformed tools**: Store transformed tools when appropriate to improve performance
6. **Add helper methods**: Provide convenient methods for common platform-specific operations
7. **Document your provider**: Include docstrings and usage examples
8. **Set meaningful provider names**: Use the name parameter for telemetry and debugging